﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Caching;

namespace Vit.Caching
{
    public class WebCacheManager : ICacheManager
    {
        private string dependentEntitySetPrefix = "dependent_entity_set_";
        List<string> keys = new List<string>();

        public WebCacheManager()
        {
        }

        #region ICacheManager Members

        public void Initialize(string cacheManagerName)
        {
            this.Name = cacheManagerName;
            dependentEntitySetPrefix = cacheManagerName + "_" + dependentEntitySetPrefix;
        }

        public void Add(string key, object value)
        {
            key = GetKey(key);
            if (!keys.Contains(key))
                keys.Add(key);
            this.HttpCache.Insert(key, value);
        }

        /// <summary>
        /// Adds the specified entry to the cache.
        /// </summary>
        /// <param name="key">The entry key.</param>
        /// <param name="value">The entry value.</param>
        /// <param name="dependentEntitySets">The list of dependent entity sets.</param>
        /// <param name="slidingExpiration">The sliding expiration - IGNORED.</param>
        /// <param name="absoluteExpiration">The absolute expiration.</param>
        public void Add(string key, object value, IEnumerable<string> dependentEntitySets, TimeSpan slidingExpiration, DateTime absoluteExpiration)
        {
            key = GetKey(key);
            if (!keys.Contains(key))
                keys.Add(key);
            foreach (var entitySet in dependentEntitySets)
            {
                this.EnsureDependancySet(dependentEntitySetPrefix + entitySet);
            }

            try
            {
                CacheDependency cd = new CacheDependency(new string[0], dependentEntitySets.Select(c => dependentEntitySetPrefix + c).ToArray());
                this.HttpCache.Insert(key, value, cd, absoluteExpiration, slidingExpiration, CacheItemPriority.Normal, null);
            }
            catch (Exception)
            {
                // there's a possibility that one of the dependencies has been evicted by another thread
                // in this case just don't put this item in the cache
            }
        }

        public bool Contains(string key)
        {
            return GetData(key) != null;
        }

        public int Count
        {
            get { return this.keys.Count; }
        }

        public void Clear()
        {
            foreach (string key in keys)
                this.HttpCache.Remove(key);
            keys.Clear();
        }

        public object GetData(string key)
        {
            key = GetKey(key);
            return this.HttpCache.Get(key);
        }

        public void Remove(string key)
        {
            key = GetKey(key);
            this.HttpCache.Remove(key);
            if (keys.Contains(key))
                keys.Remove(key);
        }

        public void RemoveDependancySet(IEnumerable<string> sets)
        {
            foreach (string entitySet in sets)
            {
                this.HttpCache.Remove(dependentEntitySetPrefix + entitySet);
            }
        }

        public object this[string key]
        {
            get { return GetData(key); }
        }

        public string Name
        {
            get;
            private set;
        }

        #endregion

        private string GetKey(string key)
        {
            return string.Format("{0}_{1}", Name, key);
        }

        private void EnsureDependancySet(string key)
        {
            var cache = this.HttpCache;

            if (cache.Get(key) == null)
            {
                try
                {
                    cache.Insert(key, key, null, Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration);
                }
                catch (Exception)
                {
                    // ignore exceptions.
                }
            }
        }

        private Cache HttpCache
        {
            get
            {
                if (HttpContext.Current != null)
                {
                    return HttpContext.Current.Cache;
                }

                return null;
            }
        }
    }
}
